library ieee;
use ieee.std_logic_1164.all;
use IEEE.NUMERIC_STD.all;
use work.typeDefinitions.all;

entity cpuStruct is
  port (
    -- clock signal
    CLK      : in  std_logic;
    -- reset for processor
    nReset   : in  std_logic;
    -- halt for processor
    halt     : out std_logic;
    ramAddr  : out std_logic_vector(15 downto 0);
    ramData  : out std_logic_vector(31 downto 0);
    ramWen   : out std_logic;
    ramRen   : out std_logic;
    ramQ     : in  std_logic_vector(31 downto 0);
    ramState : in  std_logic_vector(1 downto 0)
    );

end cpuStruct;



architecture cpuStruct_arch of cpuStruct is

  component cpuPipeline
    port (
      clk    : in std_logic;
      nReset : in std_logic;


      dMemDataR : in  std_logic_vector(31 downto 0);
      dMemWait  : in  std_logic;
      dMemRen   : out std_logic;        --!!!!!!!!!!!!!!!!!!!!!!!
      dMemDataW : out std_logic_vector(31 downto 0);
      dMemAddr  : out std_logic_vector(31 downto 0);
      dMemWen   : out std_logic;

      iMemWait : in  std_logic;
      iMemAddr : out std_logic_vector(31 downto 0);  --need more signals?
      iMemData : in  std_logic_vector(31 downto 0);
      iMemRen  : out std_logic;

      halt : out std_logic;

      forwardIn  : in  forwardSignalsIn;
      forwardOut : out forwardSignalsOut;

      instruction : out interStageOperation;
      hold        : in  stageHoldSignals;
      invalidate  : in  stageInvalidateSignals
      );
  end component;

  component StallLogic is
    port
      (
        nReset       : in  std_logic;
        icacheWait   : in  std_logic;
        dcacheWait   : in  std_logic;
        hold         : out stageHoldSignals;
        instructions : in  interStageOperation;
        invalidate   : out stageInvalidateSignals;
        forwardIn    : in  forwardSignalsOut
        );
  end component;

  component ForwardingLogic
    port
      (
        forwardIn    : in  forwardSignalsOut;
        forwardOut   : out forwardSignalsIn;
        instructions : in  interStageOperation

        );
  end component;

  component MemoryArbiterCombinational
    port
      (
        -- clk (neg-edge triggered), active-low reset
        clk, nReset     : in  std_logic;
        -- memory signals (in order)
        memAddr         : out std_logic_vector(15 downto 0);  -- mem address being read/written
        memWriteData    : out std_logic_vector(31 downto 0);  -- mem data to be written
        memWEN          : out std_logic;  -- write enable
        memREN          : out std_logic;  -- read enable
        memReadData     : in  std_logic_vector(31 downto 0);  -- mem data to be read
        memState        : in  std_logic_vector(1 downto 0);  -- state of mem
        -- icache signals (in order)
        icacheWait      : out std_logic;  -- icache must wait if 1
        icacheREN       : in  std_logic;  -- icache read request
        icacheAddr      : in  std_logic_vector(31 downto 0);  -- icache address being read
        icacheReadData  : out std_logic_vector(31 downto 0);  -- icache data to be read
                                        -- dcache signals (in order)
        dcacheREN       : in  std_logic;  -- dcache read request
        dcacheWEN       : in  std_logic;  -- dcache write request
        dcacheWait      : out std_logic;  -- dcache must wait if 1
        dcacheAddr      : in  std_logic_vector(31 downto 0);  -- dcache address being read
        dcacheReadData  : out std_logic_vector(31 downto 0);  -- dcache data to be read
        dcacheWriteData : in  std_logic_vector(31 downto 0)  -- dcache data to be written
        );
  end component;

  component dcache
    port(
      clk    : in std_logic;
      nReset : in std_logic;

      halt     : in  std_logic;
      halt_out : out std_logic;

      dMemRead      : in  std_logic;                       -- CPU side
      dMemWrite     : in  std_logic;                       -- CPU side
      dMemWait      : out std_logic;                       -- CPU side
      dMemAddr      : in  std_logic_vector (31 downto 0);  -- CPU side
      dMemDataRead  : out std_logic_vector (31 downto 0);  -- CPU side
      dMemDataWrite : in  std_logic_vector (31 downto 0);  -- CPU side

      adMemRead      : out std_logic;                       -- arbitrator side
      adMemWrite     : out std_logic;                       -- arbitrator side
      adMemWait      : in  std_logic;                       -- arbitrator side
      adMemAddr      : out std_logic_vector (31 downto 0);  -- arbitrator side
      adMemDataRead  : in  std_logic_vector (31 downto 0);  -- arbitrator side
      adMemDataWrite : out std_logic_vector (31 downto 0)   -- arbitrator side
      );

  end component;


  component icache port(
    clk, nrst : in  std_logic;
    tag_in    : in  std_logic_vector(25 downto 0);
    data_in   : in  std_logic_vector(31 downto 0);
    data_out  : out std_logic_vector(31 downto 0);
    index_in  : in  std_logic_vector(3 downto 0);
    hit       : out std_logic;
    icache_en : in  std_logic);
  end component;

  component icache_ctrl port(
    clk, nrst         : in  std_logic;
    hit               : in  std_logic;
    icache_ren        : in  std_logic;
    addr              : in  std_logic_vector(31 downto 0);
    data_in           : in  std_logic_vector(31 downto 0);
    icacheWait        : in  std_logic;
    tag               : out std_logic_vector(25 downto 0);
    index             : out std_logic_vector(3 downto 0);
    data_out          : out std_logic_vector(31 downto 0);
    addr_out          : out std_logic_vector(31 downto 0);
    cpuWait           : out std_logic;
    icache_memRequest : out std_logic;
    icache_wen        : out std_logic);
  end component;

--  component icache is
--    port(
--      clk    : in std_logic;
--      nReset : in std_logic;
--
--      iMemRead : in  std_logic;                       -- CPU side
--      iMemWait : out std_logic;                       -- CPU side
--      iMemAddr : in  std_logic_vector (31 downto 0);  -- CPU side
--      iMemData : out std_logic_vector (31 downto 0);  -- CPU side
--
--      aiMemWait : in  std_logic;                       -- arbitrator side
--      aiMemRead : out std_logic;                       -- arbitrator side
--      aiMemAddr : out std_logic_vector (31 downto 0);  -- arbitrator side
--      aiMemData : in  std_logic_vector (31 downto 0)   -- arbitrator side
--      );
--  end component;




  signal instructionCache, dataCache, iMem, dMem : cacheSignal;
  signal mem                                     : memSignal;


  signal iCntrl : icacheCntrl;

  signal instruction : interStageOperation;
  signal hold        : stageHoldSignals;
  signal invalidate  : stageInvalidateSignals;

  signal forward : forwardSignals;

  signal t_halt : std_logic;

--signal forward : forwardSignals;

begin  -- cpuStruct_arch

  ramAddr   <= mem.addr;
  ramData   <= mem.wData;
  ramWen    <= mem.wEn;
  ramRen    <= mem.rEn;
  mem.rData <= ramQ;
  mem.state <= ramState;

  instructionCache.wEN   <= '0';
  instructionCache.wData <= (others => '0');

  forw_c : ForwardingLogic port map (
    forwardIn    => forward.outs,
    forwardOut   => forward.ins,
    instructions => instruction);

  arb_c : MemoryArbiterCombinational port map (
    clk             => clk,
    nReset          => nReset,
    memAddr         => mem.addr,
    memWriteData    => mem.wData,
    memWEN          => mem.wEn,
    memREN          => mem.rEn,
    memReadData     => mem.rData,
    memState        => mem.state,
    icacheWait      => iMem.stall,
    icacheREN       => iMem.rEn,
    icacheAddr      => iMem.addr,
    icacheReadData  => iMem.rData,
    dcacheREN       => dMem.rEn,
    dcacheWEN       => dMem.wEn,
    dcacheWait      => dMem.stall,
    dcacheAddr      => dMem.addr,
    dcacheReadData  => dMem.rData,
    dcacheWriteData => dMem.wData);

  ic_c : icache port map (
    clk       => clk,
    nrst      => nReset,
    tag_in    => iCntrl.tag,
    data_in   => iMem.rData,
    data_out  => iCntrl.rData,
    index_in  => iCntrl.index,
    hit       => iCntrl.hit,
    icache_en => iCntrl.wEn);

  iccntrl_c : icache_ctrl port map (
    clk               => clk,
    nrst              => nReset,
    hit               => iCntrl.hit,
    icache_ren        => instructionCache.rEn,
    addr              => instructionCache.addr,
    data_in           => iCntrl.rData,
    icacheWait        => iMem.stall,
    tag               => iCntrl.tag,
    index             => iCntrl.index,
    data_out          => instructionCache.rData,
    addr_out          => iMem.addr,
    cpuWait           => instructionCache.stall,
    icache_memRequest => iMem.rEN,
    icache_wen        => iCntrl.wEn);

  --ic_c : icache port map (
  --  clk       => clk,
  --  nReset    => nReset,
  --  iMemRead  => instructionCache.rEn,
  --  iMemAddr  => instructionCache.addr,
  --  iMemData  => instructionCache.rData,
  --  aiMemRead => iMem.rEn,
  --  aiMemWait => iMem.stall,
  --  iMemWait  => instructionCache.stall,
  --  aiMemAddr => iMem.addr,
  --  aiMemData => iMem.rData);

  dc_c : dcache port map (
    clk            => clk,
    nReset         => nReset,
    halt           => t_halt,
    halt_out       => halt,
    dMemRead       => dataCache.rEn,
    dMemWrite      => dataCache.wEn,
    dMemAddr       => dataCache.addr,
    dMemDataRead   => dataCache.rData,
    dMemDataWrite  => dataCache.wData,
    adMemRead      => dMem.rEn,
    adMemWrite     => dMem.wEn,
    adMemWait      => dMem.stall,
    dMemWait       => dataCache.stall,
    adMemAddr      => dMem.addr,
    adMemDataRead  => dMem.rData,
    adMemDataWrite => dMem.wData);

  stall_c : StallLogic port map (
    nReset       => nReset,
    icacheWait   => instructionCache.stall,
    dcacheWait   => dataCache.stall,
    hold         => hold,
    instructions => instruction,
    invalidate   => invalidate,
    forwardIn    => forward.outs);

  
  cpu_c : cpuPipeline port map (
    clk       => clk,
    nReset    => nReset,
    dMemDataR => dataCache.rData,
    dMemWait  => dataCache.stall,
    dMemRen   => dataCache.rEn,
    dMemDataW => dataCache.wData,
    dMemWen   => dataCache.wEn,
    dMemAddr  => dataCache.addr,

    iMemData => instructionCache.rData,
    iMemRen  => instructionCache.rEn,
    iMemWait => instructionCache.stall,
    iMemAddr => instructionCache.addr,

    forwardIn  => forward.ins,
    forwardOut => forward.outs,

    halt        => t_halt,
    instruction => instruction,
    hold        => hold,
    invalidate  => invalidate);


end cpuStruct_arch;
